package indi.coffeenc.queuedownloader;

import indi.coffeenc.queuedownloader.strategy.Task;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class Downloader extends Thread{

    /**
     * 打断标志位，打断当前任务的下载
     * true : 暂停当前任务，继续下一个任务
     * false : 默认值，下载器正常运行
     */
    private boolean interrupt = false;

    /**
     * 临时字段
     * http请求方式
     */
    private String method = "GET";

    /**
     * 临时字段
     * 将内存中的数据按指定大小的块写入外存的文件中，单位MB
     * 选择合适大小的缓冲块能避免频繁的IO操作
     */
    private int blockSize = 1;

    @Override
    public void run() {
        while (true){
            /**
             * 当待下载队列为空时，下载器等待
             */
            while (TaskHolder.isEmpty()){
                synchronized (this){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            Task task = TaskHolder.get();
            this.mkdir(task.getDir());
            while(!task.isComplete()){
                Task.URL url = task.get();
                url.reset();    // 未完成断点续传功能，因此重置每个下载链接的已有进度
                InputStream in = get(url.getUrl());
                try {
                    FileOutputStream out = new FileOutputStream(task.getDir()+"/"+getFileName(url.getUrl()));
                    for (int size = 0;(size=flush(in,out))!=-1;){
                        url.flush(size);
                        /**
                         * 检测打断信号
                         * 如果检测到interrupt为true，则暂停当前任务的下载
                         */
                        if (interrupt)
                            break;
                    }
                    in.close();
                    out.close();
                    /**
                     * 检测打断信号
                     * 如果检测到interrupt为true，则暂停当前任务的下载
                     * 同时复位打断信号（设置打断信号为默认值false）
                     * 并将任务队列中的任务同步暂停
                     */
                    if (interrupt){
                        TaskHolder.pause(task.getUuid());
                        interrupt = false;
                        break;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 从输入流中读取一个块的内容到文件中
     */
    private int flush(InputStream in, FileOutputStream out) throws IOException {
        byte[] block = new byte[this.blockSize*1024*1024];
        int size = 0;
        size = in.read(block);
        if (size == -1)
            return -1;
        out.write(block,0,size);
        out.flush();
        return size;
    }

    /**
     * 从网络上下载单个文件
     */
    private InputStream request(String url,HashMap<String ,String> headers, String method) throws IOException {
        URL url0 = new URL(url);
        HttpURLConnection httpURLConnection = (HttpURLConnection)url0.openConnection();
        /**
         * 设置请求方式
         */
        httpURLConnection.setRequestMethod(method);
        /**
         * 设置请求头
         */
        if (headers != null)
            for (Map.Entry<String ,String> header : headers.entrySet()){
                httpURLConnection.setRequestProperty(header.getKey(),header.getValue());
            }
        /**
         * 与目标服务器建立TCP连接，但并未发送http请求
         */
        httpURLConnection.connect();
        /**
         * 隐式发送http请求，获取响应信息的状态码
         */
        int code = httpURLConnection.getResponseCode();
        /**
         * 请求已成功，状态码为200
         */
        if (code==200){
            return httpURLConnection.getInputStream();
        }
        System.out.println(httpURLConnection.getInputStream());
        return null;
    }

    /**
     * 发送http请求，返回输入流
     * 采用默认方法 GET
     * 采用默认头 null（暂时是这样）
     */
    private InputStream get(String url){
        try {
            HashMap<String,String> headers = new HashMap<String, String>();
            headers.put("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");
            return request(url,headers,method);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从URL中分离出文件名
     */
    private String getFileName(String url){
        String fileName = url.substring(url.lastIndexOf("/")+1);
        return fileName.substring(0,fileName.lastIndexOf("?")==-1?fileName.length():fileName.lastIndexOf("?"));
    }

    /**
     * 暂停当前任务的下载
     */
    public void pause(){
        /**
         * 如果下载器已处于休眠状态，则不进行任何操作
         */
        if(this.getState() == State.WAITING)
            return;
        this.interrupt = true;
    }

    /**
     * 唤醒休眠的下载器
     */
    public void awake(){
        if (this.getState() == State.WAITING)
            synchronized (this){
                notify();
            }
    }

    /**
     * 递归创建目录
     */
    private boolean mkdir(String dir){
        if(new File(dir).exists())
            return true;
        if(mkdir(dir.substring(0,dir.lastIndexOf("/")))){
            new File(dir).mkdir();
            return true;
        }
        return false;
    }
}
